package com.genersoft.iot.vmp.projectcamera;

import com.genersoft.iot.vmp.common.InviteSessionType;
import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IInviteStreamService;
import com.genersoft.iot.vmp.service.IPlayService;
import com.genersoft.iot.vmp.service.bean.InviteErrorCode;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.storager.dao.DeviceChannelMapper;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;
import javax.servlet.http.HttpServletRequest;
import javax.sip.InvalidArgumentException;
import javax.sip.SipException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/projectcamera")
public class CameraController {

    @Autowired
    private DeviceChannelMapper channelMapper;
    @Autowired
    private SipConfig sipConfig;
    @Autowired
    private SIPCommander cmder;
    String[] ptzType = {"未知", "球机", "半球", "固定枪机", "遥控枪机"};
    @Autowired
    private SysInfo sysInfo;

    public boolean auth(String dockingKey,String dockingScreet){
        return dockingKey.equals(sysInfo.getDockingKey())
                && dockingScreet.equals(sysInfo.getDockingScreet());
    }

    @GetMapping("/getCameraInfo")
    public Result<List<CameraInfo>> getCameraInfo(String dockingKey,
                                String dockingScreet,
                                String permitNum,
                                Integer pageIndex,
                                Integer pageSize) {
        if (!auth(dockingKey,dockingScreet))
            return new Result<>("1", "认证失败", "0", null);
        List<DeviceChannel> page = channelMapper.getByPage((pageIndex - 1) * pageSize, pageSize);
        List<CameraInfo> cameraInfos = page.stream().map(o -> {
            CameraInfo cameraInfo = new CameraInfo();
            cameraInfo.setPermitNum(o.getPermitNum());
            cameraInfo.setHandlerName(o.getHandlerName());
            cameraInfo.setHandlerPhone(o.getHandlerPhone());
            cameraInfo.setDeviceName(o.getName());
            cameraInfo.setDevicePosition(o.getName());
            cameraInfo.setFrontType(o.getPTZTypeText());
            cameraInfo.setManufacturer(o.getManufacture());
            cameraInfo.setCameraId(String.valueOf(o.getId()));
            cameraInfo.setAccessType("国标通道");
            cameraInfo.setSipNo(sipConfig.getId());
            cameraInfo.setSipField(sipConfig.getDomain());
            SimpleDateFormat f1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            SimpleDateFormat f2 = new SimpleDateFormat("yyyy-MM-dd");
            try {
                cameraInfo.setDeviceBeginDate(f2.format(f1.parse(o.getCreateTime())));
                cameraInfo.setDeviceEndDate(f2.format(f1.parse(o.getUpdateTime())));
            } catch (ParseException ignored) {
                cameraInfo.setDeviceBeginDate(f2.format(new Date()));
                cameraInfo.setDeviceEndDate(f2.format(new Date()));
            }
            return cameraInfo;
        }).collect(Collectors.toList());
        int count = channelMapper.getAllChannelCount();
        return new Result<>("0", "成功", String.valueOf(count), cameraInfos);
    }


    @Autowired
    private IInviteStreamService inviteStreamService;


    @Autowired
    private DeferredResultHolder resultHolder;

    @Autowired
    private IPlayService playService;


    private final static Logger logger = LoggerFactory.getLogger(CameraController.class);


    @Autowired
    private IVideoManagerStorage storager;
    @Autowired
    private UserSetting userSetting;

    public DeferredResult<Result<LiveURL>> play(HttpServletRequest request,
                                                String deviceId,
                                                String channelId) {

        logger.info("[开始点播] deviceId：{}, channelId：{}, ", deviceId, channelId);
        // 获取可用的zlm
        Device device = storager.queryVideoDevice(deviceId);
        MediaServerItem newMediaServerItem = playService.getNewMediaServerItem(device);

        RequestMessage requestMessage = new RequestMessage();
        String key = DeferredResultHolder.CALLBACK_CMD_PLAY + deviceId + channelId;
        requestMessage.setKey(key);
        String uuid = UUID.randomUUID().toString();
        requestMessage.setId(uuid);
        DeferredResult<Result<LiveURL>> result = new DeferredResult<>(userSetting.getPlayTimeout().longValue());

        result.onTimeout(() -> {
            logger.info("[点播等待超时] deviceId：{}, channelId：{}, ", deviceId, channelId);
            // 释放rtpserver
            Result<LiveURL> infoResult = new Result<>();
            infoResult.setCode("1");
            infoResult.setMsg("点播超时");
            requestMessage.setData(infoResult);
            resultHolder.invokeAllResult(requestMessage);
            inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, deviceId, channelId);
            storager.stopPlay(deviceId, channelId);
        });

        // 录像查询以channelId作为deviceId查询
        resultHolder.put(key, uuid, result);

        playService.play(newMediaServerItem, deviceId, channelId, null, (code, msg, data) -> {
            Result<LiveURL> wvpResult = new Result<>();
            if (code == InviteErrorCode.SUCCESS.getCode()) {
                wvpResult.setCode("0");
                wvpResult.setMsg(ErrorCode.SUCCESS.getMsg());
                wvpResult.setCount("1");

                if (data != null) {
                    StreamInfo streamInfo = (StreamInfo) data;
                    if (userSetting.getUseSourceIpAsStreamIp()) {
                        streamInfo.channgeStreamIp(request.getLocalAddr());
                    }
                    wvpResult.setData(new LiveURL(streamInfo.getHttps_hls().getUrl()));
                }
            } else {
                wvpResult.setCode(String.valueOf(code));
                wvpResult.setMsg(msg);
            }
            requestMessage.setData(wvpResult);
            resultHolder.invokeResult(requestMessage);
        });
        return result;
    }

    @GetMapping("/getCameraLiveUrl")
    public Object getCameraLiveUrl(
            HttpServletRequest request,
            String dockingKey,
            String dockingScreet,
            String cameraId) {
        if (!auth(dockingKey,dockingScreet))
            return new Result<>("1", "认证失败", "0", null);
        DeviceChannel channel = channelMapper.getByID(Integer.valueOf(cameraId));
        return play(request, channel.getDeviceId(), channel.getChannelId());
    }

    @GetMapping("/operationCloudCamera")
    public Result operationCloudCamera(String dockingKey,
                                            String dockingScreet,
                                            int direct,
                                            int step,
                                            boolean stop,
                                            String szCameraId) throws InterruptedException {
        if (!auth(dockingKey,dockingScreet))
            return new Result<>("1", "认证失败", "0", null);
        DeviceChannel channel = channelMapper.getByID(Integer.valueOf(szCameraId));
        String cmd;
        switch (direct){
            case 1 -> cmd = "up";
            case 2 -> cmd = "down";
            case 3 -> cmd = "left";
            case 4 -> cmd = "right";
            case 5 -> cmd = "upleft";
            case 6 -> cmd = "downleft";
            case 7 -> cmd = "upright";
            case 8 -> cmd = "downright";
            default -> cmd = "stop";
        }
        if (stop) {
            ptz(channel.getDeviceId(), channel.getChannelId(), cmd, step, step, step);
            Thread.sleep(300);
            ptz(channel.getDeviceId(), channel.getChannelId(), "stop", step, step, step);
        }else{
            ptz(channel.getDeviceId(), channel.getChannelId(), cmd, step, step, step);
        }
        Map<String, Object> map = new HashMap<>();
        map.put("direct",direct);
        map.put("step",step);
        map.put("stop",stop);
        map.put("szCameraId",szCameraId);
        return new Result<>("0", "成功", "1", map);
    }

    @GetMapping("/getCameraStatus")
    public Result<CameraStatus> getCameraStatus(String dockingKey,
                                  String dockingScreet,
                                  String szCameraId,
                                  String permitNum) {
        if (!auth(dockingKey,dockingScreet))
            return new Result<>("1", "认证失败", "0", null);
        DeviceChannel deviceChannel;
        if (StringUtils.hasText(szCameraId))
            deviceChannel = channelMapper.getByID(Integer.valueOf(szCameraId));
        else
            deviceChannel = channelMapper.getByPermitNum(permitNum);
        CameraStatus cameraStatus = new CameraStatus(String.valueOf(deviceChannel.getId()), deviceChannel.isStatus() ? 1 : 0);
        return new Result<>("0", "成功", "1", cameraStatus);
    }

    @PostMapping("/api/channel/changeInfo")
    public String changeInfo(Integer id,
                             String permitNum,
                             String handlerName,
                             String handlerPhone) {
        DeviceChannel deviceChannel = new DeviceChannel();
        deviceChannel.setId(id);
        deviceChannel.setPermitNum(permitNum);
        deviceChannel.setHandlerName(handlerName);
        deviceChannel.setHandlerPhone(handlerPhone);
        channelMapper.updateInfo(deviceChannel);
        return "success";
    }



    public void ptz( String deviceId, String channelId, String command, int horizonSpeed, int verticalSpeed, int zoomSpeed){

        if (logger.isDebugEnabled()) {
            logger.debug(String.format("设备云台控制 API调用，deviceId：%s ，channelId：%s ，command：%s ，horizonSpeed：%d ，verticalSpeed：%d ，zoomSpeed：%d",deviceId, channelId, command, horizonSpeed, verticalSpeed, zoomSpeed));
        }
        Device device = storager.queryVideoDevice(deviceId);
        int cmdCode = 0;
        switch (command){
            case "left":
                cmdCode = 2;
                break;
            case "right":
                cmdCode = 1;
                break;
            case "up":
                cmdCode = 8;
                break;
            case "down":
                cmdCode = 4;
                break;
            case "upleft":
                cmdCode = 10;
                break;
            case "upright":
                cmdCode = 9;
                break;
            case "downleft":
                cmdCode = 6;
                break;
            case "downright":
                cmdCode = 5;
                break;
            case "zoomin":
                cmdCode = 16;
                break;
            case "zoomout":
                cmdCode = 32;
                break;
            case "stop":
                horizonSpeed = 0;
                verticalSpeed = 0;
                zoomSpeed = 0;
                break;
            default:
                break;
        }
        try {
            cmder.frontEndCmd(device, channelId, cmdCode, horizonSpeed, verticalSpeed, zoomSpeed);
        } catch (SipException | InvalidArgumentException | ParseException e) {
            logger.error("[命令发送失败] 云台控制: {}", e.getMessage());
            throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
        }
    }
}
